
import Ice.Current;
import Optimization.*;
import Tracking.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import ws.tracking.*;
import CustomerRelationsManagement.*;

import java.io.*;

import java.net.*;
import java.util.*;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.xml.sax.SAXException;

/**
 *
 * @author Administrator
 */
public class OptimizationEngineImpl extends _OptimizationEngineDisp {

    //private String trackingServiceString = null;
	private String trackingservice = null;
	private boolean initTS = false;
	private boolean initCRM = false;
	private String crm = null;
	private final String fileprefix = "format";
    private TrackingServicePrx tsproxy;
    private CRMPrx crmproxy;
    private String filename = "format.xml";
    private String resultfilename = filename + "_result";
    private int status = 0;
    private boolean hasRun = false;
    SimpleFtpClient ftp = new SimpleFtpClient();

    /**
     *
     * @param __current
     * @throws Optimization.OptimizationEngineBusy
     */
    @Override
    public void runTest(Current __current) {
        // TODO Auto-generated method stub

    	if(hasRun)
    		return;
    	if(!initTS)
    		this.initializeTrackingService();
    	if(!initCRM)
    		this.intializeCRM();
    	runTestHelper();
        
    }

    public void runTestHelper()
    {
        if(!initTS)
    		this.initializeTrackingService();
    	if(!initCRM)
    		this.intializeCRM();
        List<Vehicle> routes = routeDataToList(tsproxy.getAllData());
        System.out.println("new routes loaded.");
        List<Order> neworders = null;
        orderPrx[] orderprx;
        try {
            orderprx = crmproxy.LoadNewOrders();
            System.out.println("new orders loaded.");
            neworders = orderDataToList(orderprx);
        } catch (DBError e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (RecordNotFound e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        try {
            FileOutputStream fos = new FileOutputStream(new File(filename));
            RouteParser.createXml(routes, neworders, fos);
//            RouteParser.createXml(routes, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
//        synchronized(this)
//        {
//            status = 0;
//            ftp.sendPacket(ftp.seqNum, filename);
//            System.out.println("packet sent");
//        }
//        synchronized(this)
//        {
//	        while(status != 2 && status != 1)
//	        {
//	            try {
//	                System.out.println("waiting...1");
//	                wait();
//	                System.out.println("finished waiting 1 (" + status + ")");
//	            } catch (InterruptedException ex) {
//	                Logger.getLogger(OptimizationEngineImpl.class.getName()).log(Level.SEVERE, null, ex);
//	            }
//	        }
//        }
//        int oldstatus = status;
//        status = 0;
//        if(oldstatus == 1)
//            throw new OptimizationEngineBusy();
        try {
            ftp.runTest(filename);
        } catch (OptimizationEngineBusy ex) {
            Logger.getLogger(OptimizationEngineImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        hasRun = true;

//        synchronized(this)
//        {
//	        while(status != 1 && status != 3 && status != 4)
//	        {
//	            try {
//	                System.out.println("waiting...2");
//	                wait();
//	                System.out.println("finished waiting 2 (" + status + ")");
//	            } catch (InterruptedException ex) {
//	                Logger.getLogger(OptimizationEngineImpl.class.getName()).log(Level.SEVERE, null, ex);
//	            }
//	        }
//        }
//
//        oldstatus = status;
//        status = 0;
//        if(oldstatus == 3)
//            throw new OptimizationEngineBusy();
    }

    /**
     *
     */
    public void test() {
        RouteData routedata;
        try {
            FileInputStream fis = new FileInputStream(new File(filename));
            RouteParser.ParseResult result = RouteParser.parse(fis);
            routedata = listToRouteData(result.routes);
            tsproxy.postNewRoutes(routedata);
            System.out.println("ROUTES POSTED");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        routedata = tsproxy.getAllData();
        List<Vehicle> routes = routeDataToList(routedata);

        for (int i = 0; i < routedata.Trucks.length; i++) {
            System.out.println(routedata.Trucks[i]);
            System.out.println(routedata.Trucks[i].capacity);
            System.out.println(routedata.Trucks[i].id);
            for (int j = 0; j < routedata.Trucks[i].stops.length; j++) {
                System.out.println(routedata.Trucks[i].stops[j]);
                System.out.println(routedata.Trucks[i].stops[j].location);
                System.out.println(routedata.Trucks[i].stops[j].actions);
                for (int k = 0; k < routedata.Trucks[i].stops[j].actions.length; k++) {
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].type);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].customerID);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].dst);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].orderID);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].src);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].weight);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].Deadline.day);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].Deadline.month);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].Deadline.year);
                    System.out.println(routedata.Trucks[i].stops[j].actions[k].type);
                }
            }
        }

        System.out.println(routes.toString());
    }
    
    public void setTrackingServiceString(String s)
    {
    	trackingservice = s;
    }
    
    public void setCRMString(String s)
    {
    	crm = s;
    }

    /**
     *
     * @return
     */
    public List<Vehicle> getAllDataTest() {
        return routeDataToList(tsproxy.getAllData());
    }

    /**
     *
     * @param orders
     * @return
     */
    public List<Order> orderDataToList(orderPrx[] orders) {
        List<Order> neworders = new ArrayList<Order>();
        Order order;
        SimpleDate date;
        orderPrx orderprx;
        CustomerRelationsManagement.datetime dtime;
        for (int i = 0; i < orders.length; i++) {
            orderprx = orders[i];
            order = new Order();
            date = new SimpleDate();
            dtime = orderprx.getDeadline();
            date.setDay(dtime.day);
            date.setMonth(dtime.month);
            date.setYear(dtime.year);
            order.setDeadline(date);
            order.setDst(orderprx.getDst());
            try {
                order.setId(orderprx.getId());
            } catch (RecordNotCommitted e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            order.setSrc(orderprx.getSrc());
            order.setWeight(orderprx.getWeight());
            neworders.add(order);
        }
        return neworders;
    }

    /**
     *
     * @param routedata
     * @return
     */
    public List<Vehicle> routeDataToList(RouteData routedata) {
        List<Vehicle> routes = new ArrayList<Vehicle>();
        Vehicle vehicle;
        Stop stop;
        Order order;
        SimpleDate simpledate;
        TruckData[] truckdatum = routedata.Trucks;
        TruckData truckdata;
        StopData[] stopdatum;
        StopData stopdata;
        StopActionData[] stopactiondatum;
        StopActionData stopactiondata;

        for (int i = 0; i < truckdatum.length; i++) {
            vehicle = new Vehicle();
            truckdata = truckdatum[i];
            vehicle.setCapacity(truckdata.capacity);
            vehicle.setId(Integer.parseInt(truckdata.id));
            stopdatum = truckdata.stops;
            for (int j = 0; j < stopdatum.length; j++) {
                stopdata = stopdatum[j];
                stop = new Stop();
                stop.setLocation(stopdata.location);
                stopactiondatum = stopdata.actions;
                for (int k = 0; k < stopactiondatum.length; k++) {
                    stopactiondata = stopactiondatum[k];
                    order = new Order();
                    simpledate = new SimpleDate();
                    simpledate.setDay((int) stopactiondata.Deadline.day);
                    simpledate.setMonth((int) stopactiondata.Deadline.month);
                    simpledate.setYear((int) stopactiondata.Deadline.year);
                    order.setDeadline(simpledate);
                    order.setDst(stopactiondata.dst);
                    order.setId(stopactiondata.orderID);
                    order.setSrc(stopactiondata.src);
                    order.setWeight(stopactiondata.weight);
                    if (stopactiondata.type == stopactiondata.type.DELIVERY) {
                        stop.getDeliveries().add(order);
                    } else {
                        stop.getPickups().add(order);
                    }
                }
                vehicle.getCurrentRoute().add(stop);
            }
            routes.add(vehicle);
        }
        return routes;
    }

    /**
     *
     * @param routes
     * @return
     */
    public RouteData listToRouteData(List<Vehicle> routes) {
        RouteData routedata = new RouteData();
        routedata.Trucks = new TruckData[routes.size()];
        TruckData truckdata;
        StopData stopdata;
        StopActionData stopactiondata;
        Vehicle vehicle;

        List<Stop> stops;
        List<Order> orders;
        Stop stop;
        Order order;
        for (int i = 0; i < routes.size(); i++) {
            vehicle = routes.get(i);
            truckdata = new TruckData();
            truckdata.capacity = (float) vehicle.getCapacity();
            truckdata.id = Integer.toString(vehicle.getId());
            stops = vehicle.getCurrentRoute();
            truckdata.stops = new StopData[stops.size()];
            for (int j = 0; j < stops.size(); j++) {
                stop = stops.get(j);
                truckdata.stops[j] = new StopData();
                stopdata = truckdata.stops[j];
                stopdata.location = stop.getLocation();
                stopdata.actions = new StopActionData[stop.getDeliveries().size() + stop.getPickups().size()];

                orders = stop.getDeliveries();

                for (int k = 0; k < orders.size(); k++) {
                    order = orders.get(k);
                    stopdata.actions[k] = new StopActionData();
                    stopactiondata = stopdata.actions[k];
                    //stopactiondata.customerID = stop.getDeliveries().get(k).getId();
                    stopactiondata.dst = order.getDst();
                    stopactiondata.orderID = order.getId();
                    stopactiondata.src = order.getSrc();
                    stopactiondata.type = StopAction.DELIVERY;
                    stopactiondata.weight = (float) order.getWeight();
                    stopactiondata.Deadline = new Tracking.datetime();
                    stopactiondata.Deadline.day = (byte) order.getDeadline().getDay();
                    stopactiondata.Deadline.month = (byte) order.getDeadline().getMonth();
                    stopactiondata.Deadline.year = (short) order.getDeadline().getYear();
                }

                orders = stop.getPickups();
                for (int k = 0; k < orders.size(); k++) {
                    order = orders.get(k);
                    stopdata.actions[k + stop.getDeliveries().size()] = new StopActionData();
                    stopactiondata = stopdata.actions[k + stop.getDeliveries().size()];
                    //stopactiondata.customerID = stop.getDeliveries().get(k).getId();
                    stopactiondata.dst = order.getDst();
                    stopactiondata.orderID = order.getId();
                    stopactiondata.src = order.getSrc();
                    stopactiondata.type = StopAction.PICKUP;
                    stopactiondata.weight = (float) order.getWeight();
                    stopactiondata.Deadline = new Tracking.datetime();
                    stopactiondata.Deadline.day = (byte) order.getDeadline().getDay();
                    stopactiondata.Deadline.month = (byte) order.getDeadline().getMonth();
                    stopactiondata.Deadline.year = (short) order.getDeadline().getYear();
                }
            }
            routedata.Trucks[i] = truckdata;
        }
        return routedata;

    }

    /**
     *
     * @param trackingServiceString
     */
    public void initializeTrackingService() {
        //this.trackingServiceString = trackingServiceString;

        Ice.Communicator communicator = null;

        try {
            //
            // Boilerplate code for starting up the Ice runtime
            //
            Ice.Properties properties = Ice.Util.createProperties();
            properties.load("config_client");
            Ice.InitializationData id = new Ice.InitializationData();
            id.properties = properties;
            communicator = Ice.Util.initialize(id);

            String objectString = trackingservice;
            Ice.ObjectPrx base = communicator.stringToProxy(objectString);
            tsproxy = TrackingServicePrxHelper.checkedCast(base.ice_twoway().ice_timeout(-1).ice_secure(false));
            if (tsproxy == null) {
                System.err.println("invalid proxy");
                System.exit(1);
            }
            initTS = true;
        } catch (Ice.LocalException ex) {
            ex.printStackTrace();
            System.exit(1);
        }

//		finally
//		{
//			// 
//			// Shutdown the communicator, if it exists
//			//
//			System.err.println("Shutting down Ice communicator");
//			if (communicator != null)
//			{
//				try
//				{
//					communicator.destroy();
//				}
//				catch (Ice.LocalException ex)
//				{
//					ex.printStackTrace();
//					System.exit(1);
//				}
//			}
//		}
    }

    /**
     *
     * @param crmServiceString
     */
    public void intializeCRM() {
        //this.trackingServiceString = trackingServiceString;

        Ice.Communicator communicator = null;

        try {
            //
            // Boilerplate code for starting up the Ice runtime
            //
            Ice.Properties properties = Ice.Util.createProperties();
            properties.load("config_client");
            Ice.InitializationData id = new Ice.InitializationData();
            id.properties = properties;
            communicator = Ice.Util.initialize(id);

            String objectString = crm;
            Ice.ObjectPrx base = communicator.stringToProxy(objectString);
            crmproxy = CRMPrxHelper.checkedCast(base.ice_twoway().ice_timeout(-1).ice_secure(false));
            if (crmproxy == null) {
                System.err.println("invalid proxy");
                System.exit(1);
            }
            initCRM = true;
        } catch (Ice.LocalException ex) {
            ex.printStackTrace();
            System.exit(1);
        }


//		finally
//		{
//			// 
//			// Shutdown the communicator, if it exists
//			//
//			System.err.println("Shutting down Ice communicator");
//			if (communicator != null)
//			{
//				try
//				{
//					communicator.destroy();
//				}
//				catch (Ice.LocalException ex)
//				{
//					ex.printStackTrace();
//					System.exit(1);
//				}
//			}
//		}
    }

    private void notifyCompleteOpt() {

        try {
            FileInputStream fis = new FileInputStream(new File(resultfilename));
            RouteParser.ParseResult result = RouteParser.parse(fis);
            fis.close();
            tsproxy.postNewRoutes(listToRouteData(result.routes));
            System.out.println("new routes posted");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        filename = fileprefix + ftp.seqNum + ".xml";
        resultfilename = filename + "_result";
        runTestHelper();
    }
    
    public void injectRoutes()
    {
        
		try {
			FileInputStream fis = new FileInputStream(new File(filename));
			RouteParser.ParseResult result = RouteParser.parse(fis);
			fis.close();
	        tsproxy.postNewRoutes(listToRouteData(result.routes));
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
    }

    /**
     */
    private class SimpleFtpClient {

        /**
         *
         */
        public static final int DATA_INPUT_PORT = 8900;
        /**
         *
         */
        public static final int DATA_OUTPUT_PORT = 8901;
        /**
         *
         */
        public static final int FTP_SERVER_PORT = 8902;
        /**
         *
         */
        public static final String DATA_OUTPUT_GROUP = "234.2.3.4";
        /**
         * Last sequence number completed by the server
         */
        private int complete = -1;
        private boolean busy = false;
        private boolean running = false;
        private int seqNum = 0;

        public SimpleFtpClient() {
            new MulticastListener().start();
        }

        /**
         *
         * @param filename
         * @throws Optimization.OptimizationEngineBusy
         */
        public void runTest(String filename) throws OptimizationEngineBusy {
            Socket s = null;
            try {
                s = new Socket("localhost", FTP_SERVER_PORT);
                PrintWriter pw = new PrintWriter(s.getOutputStream());
                Scanner scanner = new Scanner(s.getInputStream());
                String line = scanner.nextLine();
                System.out.println(line);

                //      pw.println("PWD");
                //      pw.flush();
                //      line = scanner.nextLine();
                //      System.out.println(line);

                System.out.println("sending PASV request");
                pw.println("PASV");
                pw.flush();
                line = scanner.nextLine();
                System.out.println(line);
                // should be (a, b, c, d, e, f)
                // where port number is 256e + f
                int i = line.indexOf("(");
                if (i >= 0) {
                    line = line.substring(i + 1, line.length() - 1);
                    //System.out.println(line);
                    String[] h = line.split(",");
                    int p = Integer.parseInt(h[4]) * 256 + Integer.parseInt(h[5]);
                    System.out.println("found port " + p);
                    System.out.println("sending STOR request");
                    pw.println("STOR " + filename);
                    pw.flush();
                    line = scanner.nextLine();
                    System.out.println(line);
                    if (line.startsWith("150")) {
                        Socket temp = new Socket("localhost", p);
                        FileInputStream fis = new FileInputStream(filename);
                        OutputStream os = temp.getOutputStream();
                        int c;
                        while ((c = fis.read()) != -1) {
                            os.write(c);
                        }
                        os.flush();
                        os.close();
                        fis.close();

                        // should get a 226 message now
                        line = scanner.nextLine();
                        System.out.println(line);
                    }
                }
                pw.flush();
                pw.close();

                // send request with sequence number 42
                sendPacket(seqNum, filename);

                // wait for completion message
//                synchronized (this) {
//                    while (complete < OptimizationEngineImpl.this.seqNum && !busy) {
//                        try {
//
//                            wait();
//                        } catch (InterruptedException ignore) {
//                        }
//                    }
//                }
            //      pw.println("LIST");
            //      pw.flush();
            //      line = scanner.nextLine();
            //      System.out.println(line);
            } catch (IOException e) {
                System.out.println(e);
            } finally {
                try {
                    s.close();
                    System.out.println("CLOSED SOCKET");
                } catch (IOException ignore) {
                }
            }
        }

        public void notifyComplete()
        {
            Socket s = null;
            try {
                s = new Socket("localhost", FTP_SERVER_PORT);
                PrintWriter pw = new PrintWriter(s.getOutputStream());
                Scanner scanner = new Scanner(s.getInputStream());
                String line = scanner.nextLine();
                System.out.println(line);
                System.out.println("sending PASV request");
                pw.println("PASV");
                pw.flush();
                line = scanner.nextLine();
                System.out.println(line);
                // should be (a, b, c, d, e, f)
                // where port number is 256e + f
                int i = line.indexOf("(");
                if (i >= 0) {
                    line = line.substring(i + 1, line.length() - 1);
                    System.out.println(line);
                    String[] h = line.split(",");
                    int p = Integer.parseInt(h[4]) * 256 + Integer.parseInt(h[5]);
                    System.out.println("found port " + p);
                    String resultFilename = filename + "_result";
                    System.out.println("sending RETR request for " + resultFilename);
                    pw.println("RETR " + resultFilename);
                    pw.flush();
                    line = scanner.nextLine();
                    System.out.println(line);
                    if (line.startsWith("150")) {
                        Socket temp = new Socket("localhost", p);
                        InputStream is = temp.getInputStream();
                        Scanner tempScanner = new Scanner(is);
                        FileWriter fw = new FileWriter(new File(resultFilename));
                        PrintWriter pwf = new PrintWriter(fw);
                        while (tempScanner.hasNextLine()) {
                            pwf.println(tempScanner.nextLine());
                        }
                        tempScanner.close();
                        pwf.flush();
                        pwf.close();
                        fw.close();
                        // should get a 226 message now
                        line = scanner.nextLine();
                        System.out.println(line);
                    }
                }
                pw.close();
                //synchronized(this) {
                    seqNum++;
                //}
            } catch (UnknownHostException ex) {
                Logger.getLogger(OptimizationEngineImpl.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(OptimizationEngineImpl.class.getName()).log(Level.SEVERE, null, ex);
            }

            finally {
                try {
                    s.close();
                    System.out.println("CLOSED SOCKET");
                    notifyCompleteOpt();
                } catch (IOException ex) {
                    Logger.getLogger(OptimizationEngineImpl.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        public void sendPacket(int sequenceNumber, String filename) {
            try {
                InetAddress addr = InetAddress.getByName("localhost");
                DatagramSocket s = new DatagramSocket();

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                DataOutputStream dos = new DataOutputStream(baos);
                dos.writeInt(sequenceNumber);

                if (filename != null) {
                    for (int i = 0; i < filename.length(); ++i) {
                        dos.writeByte(filename.charAt(i));
                    }
                    dos.writeByte(0); // null terminator
                }

                byte[] buf = baos.toByteArray();
                DatagramPacket packet = new DatagramPacket(buf, buf.length, addr, 8900);
                s.send(packet);
            } catch (IOException e) {
                System.out.println(e);
            }
        }

        private class MulticastListener extends Thread {

            public void run() {
                while (true) {
                    MulticastSocket socket = null;
                    try {
                        socket = new MulticastSocket(DATA_OUTPUT_PORT);
                        InetAddress group = InetAddress.getByName(DATA_OUTPUT_GROUP);
                        socket.joinGroup(group);
                        byte[] buf = new byte[8];
                        while (true) {
                            DatagramPacket packet = new DatagramPacket(buf, buf.length);
                            System.out.println("Datagram listener: listening on " + DATA_OUTPUT_PORT);
                            socket.receive(packet);
                            buf = packet.getData();
                            if (packet.getLength() == 8) {
                                DataInputStream dis = new DataInputStream(new ByteArrayInputStream(buf));
                                int seq = dis.readInt();
                                int code = dis.readInt();
                                System.out.println("Datagram listener: got sequence number " + seq +
                                        " result Ox" + Integer.toHexString(code));
                                synchronized (SimpleFtpClient.this) {
                                    synchronized (OptimizationEngineImpl.this)
                                    {
                                        if ((code == 0x300)) {
                                            complete = seq;
                                            OptimizationEngineImpl.this.status = 3;
                                            OptimizationEngineImpl.this.notifyAll();
                                        }
                                        if ((code == 0x100)) {
                                            complete = seq;
                                            OptimizationEngineImpl.this.status = 1;
                                            OptimizationEngineImpl.this.notifyAll();
                                        }
                                        if ((code == 0x400)) {
                                            complete = seq;
                                            OptimizationEngineImpl.this.status = 4;
                                            OptimizationEngineImpl.this.notifyAll();
                                        }
                                        if (code == 0x200) {
                                        	System.out.println(status);
                                            //if(status == 0)
                                            //{
                                                complete = seq;
                                                OptimizationEngineImpl.this.status = 2;
                                                notifyComplete();
                                                OptimizationEngineImpl.this.notifyAll();
                                            //}
                                        }
                                    }
                                }
                            } else {
                                System.out.println("Datagram listener: got unexpected packet length " + packet.getLength());
                                for (int i = 0; i < packet.getLength(); ++i) {
                                    System.out.print(buf[i] + " ");
                                }
                                System.out.println();
                            }
                        }

                    } catch (IOException e) {
                        System.out.println(e);
                    } finally {
                        if (socket != null) {
                            socket.close();
                        }
                    }
                    // wait a bit before trying again
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ignore) {
                    }

                }
            }
        }
    }
}

